{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Quanlse Scheduler\n",
    "\n",
    "*Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Outline\n",
    "This tutorial introduces how to use **QuanlseScheduler** to generate pulse sequences for quantum circuits. The outline of this tutorial is as follows:\n",
    "- Introduction\n",
    "- Rules in scheduling the gates\n",
    "- Example: generate a pulse sequence to implement a $W$-state by using QuanlseScheduler\n",
    "- Summary\n",
    "\n",
    "## Introduction\n",
    "\n",
    "**QuanlseScheduler** is a module that allows for automatic generation of fidelity-optimized and scheduled pulse sequence for a given quantum circuit set to perform a certain quantum computing task.\n",
    "\n",
    "**QuanlseScheduler** has the following benefits:\n",
    "\n",
    "- Highly automatic: it generates high-fidelity pulses automatically and simultaneously minimizes the overall gate operation time.\n",
    "- Practical: it considers several limitations of the superconducting quantum system, including leakage errors, decoherence, etc.\n",
    "- Flexible: it gives users the freedom to customize qubits and circuit parameters. This can also be easily extended to other quantum computing platforms.\n",
    "\n",
    "**QuanlseScheduler** achieves the following goals:\n",
    "\n",
    "- It generates parameters and AWG input signal arrays for fidelity-optimized pulses when leakage into the state $|2\\rangle$ is taken into account.\n",
    "- It is also capable of scheduling pulses to minimize idle time and therefore reduce decoherence losses.\n",
    "- At the same time, it allows for the visualization of pulse sequences for the users to check the results.\n",
    "\n",
    "CNOT gate is rarely directly implemented on superconducting quantum chips. Instead, it is often constructed by piecing together single-qubit gates and other two-qubit gates like CR gate or ISWAP gate that can be easily implemented on a superconducting chip (often called native gates). The two-qubit gates that are available in the transmon-like superconducting qubit architecture can be divided into two categories:\n",
    "\n",
    "**Flux-controlled**\n",
    "\n",
    "This class of gates offers the advantage of short gate time to minimize decoherence error. However, tuning the qubit frequency can introduce flux noises and lead to the problem of frequency crowding.\n",
    "\n",
    "**All-microwave control**\n",
    "\n",
    "CR gate allows for an all-microwave control, which alleviates the problem of flux noise. However, the much longer time-scale limits the gate fidelity (because of the decoherence effects of qubits).\n",
    "\n",
    "![cnot](figures/cnot-gate.png)\n",
    "\n",
    "Since CZ gates can be used to construct a CNOT gate easily by using only two other single-qubit gates, QuanlseScheduler offers this way to construct a CNOT gate in a quantum circuit."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Rules in scheduling the gates\n",
    "\n",
    "All gates, both single and two-qubit gates, can be scheduled by **QuanlseScheduler**, and they must obey the following rules to maximize the overall fidelity.\n",
    "\n",
    "- A two-qubit gate can only start when both qubits have completed gates scheduled before it.\n",
    "- Because of the decoherent properties of the qubits, the total gate operation time must be as short as possible to reduce idle time and therefore minimize gate errors."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example: Generate a pulse sequence to implement a $W$-state by using QuanlseScheduler\n",
    "\n",
    "In 2000, Dür et al. found that a tripartite system can be entangled in a different way from GHZ (Greenberger-Horne-Zeilinger) state, which they called $W$-state \\[4\\]:\n",
    "\n",
    "$$\n",
    "|\\Phi_{\\rm GHZ}\\rangle = \\frac{1}{\\sqrt{2}} (|000\\rangle + |111\\rangle), \\\\\n",
    "|\\Phi_{\\rm W}\\rangle = \\frac{1}{\\sqrt{3}} (|001\\rangle + |010\\rangle + |100\\rangle).\n",
    "$$\n",
    "\n",
    "Unlike the GHZ state, the remaining $(N-1)$-qubit state is still entangled if one of the qubit is traced out for a prepared $N$-qubit $W$-state. A three-partite $W$-state is a superposition state of three eigenstates, and only one of the particles is excited in each eigenstate. Due to its high robustness against loss, $W$-state received considerable attention in the field of quantum communication.\n",
    "\n",
    "In this example, we demonstrate the codes to generate a $W$-state with QuanlseScheduler. Before starting the main steps, we import the dependencies:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import math\n",
    "import numpy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we initialize a `QuanlseEnv` object and pass the parameters:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import the Quantum Environment class\n",
    "from Quanlse.QuanlseEnv import QuanlseEnv\n",
    "from Quanlse import BackendName, Algorithm, HardwareImplementation\n",
    "\n",
    "# Cretae a Quantum Environment object and define a backend\n",
    "env = QuanlseEnv()\n",
    "env.backend(BackendName.CloudScheduler, Algorithm.Normal, HardwareImplementation.CZ, tStep=1.0)\n",
    "\n",
    "# Import Define class and set the token\n",
    "# Please visit http://quantum-hub.baidu.com\n",
    "from Quanlse import Define\n",
    "Define.hubToken = ''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "those parameters are\n",
    "- `BackendName`: `BackendName.CloudScheduler` is the only option at present, which indicates that the **QuanlseScheduler** job is set to run on the Quanlse Cloud Service.\n",
    "- `Algorithm`: `Algorithm.Normal` is the only option at present, which indicates that the normal simulation algorithm is chosen.\n",
    "- `HardwareImplementation`: `HardwareImplementation.CZ` is the only option at present, which indicates that the **QuanlseScheduler** will decompose two-qubit gates into a combination of controlled-Z gates and single-qubit gates. \n",
    "- `tStep`: the stepsize of piecewise-constant quantum simulation algorithm, the unit is in ns.\n",
    "\n",
    "Then, we set up the qubit parameters. In the following codes, we define a system consisting of three three-level superconducting qubits. We pass a list of `Tuple`s as the parameters to the function `env.setupQReg()`. In each `Tuple`, the first item is the qubit frequency, and the second is the coupling strengh. The unit is in Gigahertz:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define the qubit frequency and anharmonicity\n",
    "env.setupQReg([\n",
    "    (5.805 * 2 * math.pi, -0.226 * 2 * math.pi),  # QReg 0 (GHz)\n",
    "    (5.205 * 2 * math.pi, -0.226 * 2 * math.pi),  # QReg 1 (GHz)\n",
    "    (4.605 * 2 * math.pi, -0.226 * 2 * math.pi),  # QReg 2 (GHz)\n",
    "])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next step is defining the coupling strength of the superconducting qubits, users can pass the effective coupling strength to the function `env.setupCoupling()` together with the `Tuple`s of superconducting qubit indices, anharmonicities and the optimization bounds of pulse amplitudes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define the coupling strength, as well as the optimization bounds for the flux amplitudes\n",
    "#   when the pulse acts on the corresponding qubit pairs.\n",
    "env.setupCoupling([\n",
    "    ((0, 1), 0.0277 * 2 * math.pi, (-3, 0)),\n",
    "    ((1, 2), 0.0277 * 2 * math.pi, (-3, 0)),\n",
    "    ((2, 1), 0.0277 * 2 * math.pi, (-3, 0)),\n",
    "])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The input of `setupCoupling` is a `List` of `Tuple`s, and each `Tuple` indicates a coupling (one-way); the first item saves the indices of the control and target qubits; the second item saves the efficient coupling strength and the third saves the bound of amplitude during the two-qubit gate optimization.\n",
    "\n",
    "The critical step is construct the quantum logic circuit. In this example, we use the following circuit to generate a $W$-state:\n",
    "\n",
    "![w](figures/w-circuit.png) \n",
    "\n",
    "where\n",
    "\n",
    "$$\n",
    "R_1 = \\frac{1}{\\sqrt{3}} \\begin{bmatrix} \\sqrt{2} & -1 \\\\ 1 & \\sqrt{2} \\end{bmatrix}, \n",
    "R_2 = \\begin{bmatrix} \\cos(\\pi/8) & -\\sin(\\pi/8) \\\\ \\sin(\\pi/8) & \\cos(\\pi/8) \\end{bmatrix}, \n",
    "R_3 = \\begin{bmatrix} \\cos(\\pi/8) & \\sin(\\pi/8) \\\\ -\\sin(\\pi/8) & \\cos(\\pi/8) \\end{bmatrix}.\n",
    "$$\n",
    "\n",
    "As we mentioned before, in superconducting computing, controlled-Z gate is a native gate, it can be used to implement a CNOT gate with 2 more Hadamard gates. Especially, the two Hadamard gates should be applied on the target gates, i. e.,\n",
    "\n",
    "![VQE](figures/cnot-gate-hadamard.png) \n",
    "\n",
    "In this example, we use this decomposition criteria for the CNOT gates. In **QuanlseScheduler**, the logic quantum circuit can be defined by the following codes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import the gate definition functions\n",
    "from Quanlse.QOperation.FixedGate import X, H, CZ\n",
    "from Quanlse.QOperation.RotationGate import U\n",
    "\n",
    "# R1\n",
    "U(theta=-1.231, phi=0, lamda=0)(env.Q[0])\n",
    "\n",
    "# X gate\n",
    "X(env.Q[0])\n",
    "\n",
    "# CNOT: 0 -> 1\n",
    "H(env.Q[1])\n",
    "CZ(env.Q[0], env.Q[1])\n",
    "H(env.Q[1])\n",
    "\n",
    "# X gate\n",
    "X(env.Q[0])\n",
    "\n",
    "# R2\n",
    "U(theta=-0.785, phi=0, lamda=0)(env.Q[2])\n",
    "\n",
    "# CNOT: 1 -> 2\n",
    "H(env.Q[2])\n",
    "CZ(env.Q[1], env.Q[2])\n",
    "H(env.Q[2])\n",
    "\n",
    "# R3\n",
    "U(theta=0.785, phi=0, lamda=0)(env.Q[2])\n",
    "\n",
    "# CNOT: 2 -> 1\n",
    "H(env.Q[1])\n",
    "CZ(env.Q[1], env.Q[2])\n",
    "H(env.Q[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that we decompose the rotation gates $R_1, R_2$ and $R_3$ using `U(theta, phi, lamda)`, where\n",
    "$$\n",
    "U(\\theta, \\phi, \\lambda) = e^{i(\\phi/2+\\lambda/2)} R_z(\\phi) R_y(\\theta) R_z(\\lambda) =\n",
    "\\begin{bmatrix} \n",
    "    \\cos(\\theta/2) & - e^{i\\lambda} \\sin(\\theta/2) \\\\\n",
    "    e^{i\\phi} \\sin(\\theta/2) & e^{i(\\phi + \\lambda)} \\cos(\\theta/2)\n",
    "\\end{bmatrix}\n",
    "$$\n",
    "\n",
    "Then we submit the job we defined above to Quanlse Cloud Service:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# submit jobs onto Quanlse Cloud Service\n",
    "taskResult = env.commit()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On the Quanlse Cloud Service, **QuanlseScheduler** will first generate the pulse sequences for the necessary quantum gates using **QuanlseMaker**, and automatically schedule the pulse sequences according to the quantum logic circuit. Then the \"quantum circuit\" formed by the pulse sequences will be pass into the simulator and simulate the evolution under the defined pulse sequences. The infidelity between the simulated unitary matrix and the goal unitary matrix can be calculated:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Benchmarking infidelity: \", taskResult[\"results\"][\"benchmark\"][\"infidelity\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can use the function `plotScheduler` to plot the pulse sequence:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import the function for plot scheduler result\n",
    "from Quanlse.Utils.Plot import plotScheduler\n",
    "\n",
    "# Plot the pulse sequences\n",
    "plotScheduler(taskResult[\"results\"][\"scheduler\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we use the simulated unitary matrix to calculate the population of every eigenstate:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import the operator for generating basis vector\n",
    "from Quanlse.Utils.Operator import basis\n",
    "\n",
    "# Translate the JSON matrix to numpy complex matrix\n",
    "from Quanlse.QPlatform.Utilities import dictMatrixToNumpyMatrix\n",
    "\n",
    "# Calculate the population of the eigenstates\n",
    "unitary = dictMatrixToNumpyMatrix(taskResult[\"results\"][\"benchmark\"][\"unitary\"], valueType=complex)\n",
    "finalState = (unitary @ numpy.array(basis(27, 0))).T[0]\n",
    "popList = [abs(item ** 2) for item in finalState]\n",
    "\n",
    "# Import the operator for generating basis string list\n",
    "from Quanlse.Utils.Tools import computationalBasisList\n",
    "\n",
    "# Import the function for plot bar figures\n",
    "from Quanlse.Utils.Plot import plotBarGraph\n",
    "\n",
    "# Plot the population of computational basis\n",
    "basis = computationalBasisList(3, 3)\n",
    "plotBarGraph(basis, popList, \"Population of a W state generated by QuanlseScheduler\",\n",
    "             \"Computational Basis\", \"Population\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "After reading this tutorial on QuanlseScheduler, the users are encouraged to try implementing other quantum circuits using a similar procedure as this tutorial.\n",
    "## References\n",
    "\n",
    "\\[1\\] [Krantz, Philip, et al. \"A quantum engineer's guide to superconducting qubits.\" *Applied Physics Reviews* 6.2 (2019): 021318.](https://aip.scitation.org/doi/abs/10.1063/1.5089550)\n",
    "\n",
    "\\[2\\] https://en.wikipedia.org/wiki/Quantum_optimization_algorithms\n",
    "\n",
    "\\[3\\] https://en.wikipedia.org/wiki/Quantum_algorithm\n",
    "\n",
    "\\[4\\] [Dür, Wolfgang, Guifre Vidal, and J. Ignacio Cirac. \"Three qubits can be entangled in two inequivalent ways.\" *Physical Review A* 62.6 (2000): 062314.](https://doi.org/10.1103/PhysRevA.62.062314)\n",
    "\n",
    "\\[5\\] [Guo, Guang-Can, and Yong-Sheng Zhang. \"Scheme for preparation of the W state via cavity quantum electrodynamics.\" *Physical Review A* 65.5 (2002): 054302.](https://doi.org/10.1103/PhysRevA.65.054302)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}